<?php

namespace Core\Application;

use Core\Http\Request;
use Core\Http\Response;
use Core\Route\MiddlewareExecute;
use Core\Route\RouteCollector;
use Core\Route\SimpleDispatcher;
use ReflectionMethod;

/**
 * 使用说明
 *  step 1 创建一个 dispatcher 实例
 *  setp 2 创建 dispatcher 并注入路由配置信息
 *  setp 3 接收请求并使用 dispatch 处理
 */
class Dispatcher
{
    protected $dispatcher;

    protected $requestMethod;

    protected $requestUri;

    protected $middleWare;

    public function __construct($routes = null)
    {
        $this->dispatcher = $this->createDispathcer($routes);
        $this->middleWare = new MiddlewareExecute();
    }

    /**
     * create dispatcher
     *
     * @param array     $route      路由信息
     */
    public function createDispathcer(array $routes = [])
    {
        // 初始化路由调度器
        $dispatcher = SimpleDispatcher::create(function (RouteCollector $r) use ($routes) {
            foreach ($routes as $handler) {
                $handler($r);
            }
        });

        return $dispatcher;
    }

    /**
     * router dispatch
     * 返回response对象或者null
     */
    public function dispatch()
    {
        $uri = $this->requestUri;
        $method = $this->requestMethod;

        // Strip query string (?foo=bar) and decode URI
        if (false !== $pos = strpos($uri, '?')) {
            $uri = substr($uri, 0, $pos);
        }
        $uri = rawurldecode($uri);

        // 处理请求([state, handler, args, middle])
        $routeInfo = $this->dispatcher->dispatch($method, $uri);
        // print route info
        // echo "route_info: " . json_encode($routeInfo) . PHP_EOL;

        // 执行中间件
        $res = $this->runMiddleware($routeInfo);
        if ($res === true) {
            if ($routeInfo[0] == \Core\Route\Dispatcher::FOUND) {
                $this->dispatcherFound($routeInfo);
            } else if ($routeInfo[0] == \Core\Route\Dispatcher::METHOD_NOT_ALLOWED) {
                $this->dispatcherNotAllowed($routeInfo);
            } else {
                $this->dispathcerNotFound($routeInfo);
            }
        }

        $this->runMiddlewareAfter();
    }

    /**
     * 404 Not Found
     */
    public function dispathcerNotFound(array $routeInfo)
    {
        Response::getInstance()->fail('Not Found', 404);
    }

    /**
     * 405 Method Not Allowed
     */
    public function dispatcherNotAllowed(array $routeInfo)
    {
        Response::getInstance()->fail('Method Not Allowed', 405);
    }

    /**
     * Dispatcher Found
     */
    public function dispatcherFound(array $routeInfo)
    {
        $response = null;

        $handler    = $routeInfo[1];
        $vars       = $routeInfo[2];
        // $middle     = $routeInfo[3];

        if (!is_callable($handler)) {
            $reflectionMethod = new ReflectionMethod($handler[0], $handler[1]);

            // 执行注解类
            $attrs = $reflectionMethod->getAttributes();
            foreach ($attrs as $attr) {
                $attr->newInstance();
            }

            try {
                $response = $reflectionMethod->invokeArgs(new $handler[0], $vars);
            } catch (\Error $e) {
                Response::getInstance()->fail(
                    $e->getMessage(),
                    Request::getInstance()->getRequestId()
                );
            }
        } else {
            $response = $handler($vars);
        }

        return $response;
    }

    /**
     * 执行中间件
     */
    protected function runMiddleware(array $routeInfo)
    {
        if (count($routeInfo) >= 3) {
            return $this->middleWare->run($routeInfo[3] ?: []);
        }

        return true;
    }

    /**
     * 执行中间件
     */
    public function runMiddlewareAfter()
    {
        return $this->middleWare->runAfter();
    }

    /**
     * get request method
     */
    public function setRequestMethod($method)
    {
        $this->requestMethod = $method;
        return $this;
    }

    /**
     * get request uri
     */
    public function setRequestUri($uri)
    {
        $this->requestUri = $uri;
        return $this;
    }
}
